

#include "streambuffer.h"
#include <stdio.h>
#include <string.h>
#include "stdlib.h"
//--------------------------------------------------
#define pvPortMalloc    malloc
#define vPortFree       free
#define LOG(...)		printf(__VA_ARGS__)
//--------------------------------------------------

uint16 tlsAlignSizeGet(uint16 usSize)
{
	uint8 ucCmpValue = usSize & 0x03;

	if (ucCmpValue == 0) {
		return usSize;
	}else {
		return ((4 - ucCmpValue) + usSize);
	}
}
/**
 * @fn
 * @brief   Get stream buffer one item length
 *			byte3-byte2-byte1-byte0 (high bit endian.)
 * @param
 * @return
 */
uint32 prvStreamBufferItemSizeGet(StreamBufferDef *pxStream, uint16 usStartPoint)
{
	uint32 ulItemSize = 0;

	for (uint8 i = 0; i < 4; i++)
	{

		ulItemSize <<= 8;
		ulItemSize += pxStream->pcBuffer[usStartPoint];
		usStartPoint++;

		if (usStartPoint >= pxStream->usSize)
		{
			usStartPoint = 0;
		}
	}

	return ulItemSize;
}

/**
 * @fn
 * @brief
 * @param
 * @return
 */
/*!disuse*/
StreamStatusDef xStreamBufferStaticCreate(StreamBufferDef *pxStream, uint8 *pcBuf, uint16 usSzie)
{

	/* Setting space is too small. */
	if (usSzie < 16)
	{
		return eSTREAM_SPACE_NOT_ENOUGH;
	}

	/* Buffer must 4byte alignment. */
	if (((uint32)(pcBuf)) % 4)
	{
		LOG("stream create must 4byte alignment.\r\n");
		return eSTREAM_NOT_ALIGNMEMT;
	}

	/* Create queue. */
	if (pxStream != NULL)
	{
		pxStream->pcBuffer = pcBuf;
		pxStream->usSize = usSzie;
		pxStream->ucNfItems = 0;

		pxStream->ucRx = 0;
		pxStream->ucWr = 0;

		return eSTREAM_NOT_ERR;
	}
	else
	{
		return eSTREAM_BUFFER_IS_INVALID;
	}
}

/**
 * @fn
 * @brief
 * @param
 * @return   */
StreamStatusDef xStreamBufferCreate(StreamBufferDef *pxStream, uint16 usSzie)
{
	uint8 *pcMem;

	/* Setting space is too small. */
	if (usSzie < 16)
	{
		return eSTREAM_SPACE_NOT_ENOUGH;
	}

	if (pxStream == NULL)
	{
		return eSTREAM_BUFFER_IS_INVALID;
	}

	/* alloc dynamic memory. */
	pcMem = (uint8 *)pvPortMalloc(usSzie);

	if (pcMem != NULL)
	{

		pxStream->pcBuffer = pcMem;
		pxStream->usSize = usSzie;
		pxStream->ucNfItems = 0;

		pxStream->ucRx = 0;
		pxStream->ucWr = 0;

		return eSTREAM_NOT_ERR;
	}
	else
	{
		return eSTREAM_DYNAMIC_MEM_ERROR;
	}
}

/**
 * @fn
 * @brief
 * @param
 * @return
 */
StreamStatusDef vStreamBufferDestroy(StreamBufferDef *pxStream)
{
	if (pxStream != NULL)
	{

		if (pxStream->usSize != 0)
		{

			if (pxStream->pcBuffer != NULL)
			{
				vPortFree(pxStream->pcBuffer);
				LOG("stream buffer clear.\r\n");
			}

			/* Clear buffer. */
			memset(pxStream, 0, sizeof(StreamBufferDef));

			/* buffer to empty */
			pxStream->pcBuffer = NULL;
		}

		return eSTREAM_NOT_ERR;
	}
	else
	{
		return eSTREAM_BUFFER_IS_INVALID;
	}
}

/**
 * @fn
 * @brief   Ê£Óà¿Õ¼ä
		Example:
		If total space = 128 , in order to WR not equal RX , can use maximum space = 128-1
		remain = (128 - 1)(can use maximum) - 4(one head)

 * @param
 * @return
 */
uint16 usStreamBufferFreeGet(StreamBufferDef *pxStream)
{
	uint16 usFreeSpace = 0;

	/* Protect queue write. */
	if (pxStream->usSize < 16)
	{
		return 0;
	}

	/* not any content. */
	if (pxStream->ucWr == pxStream->ucRx)
	{
		usFreeSpace = pxStream->usSize - 1 - 4;
	}
	/* have content */
	else if (pxStream->ucWr > pxStream->ucRx)
	{
		usFreeSpace = pxStream->usSize - 1 - (pxStream->ucWr - pxStream->ucRx) - 4;
	}
	/* flowover. if (pxStream->ucWr < pxStream->ucRx) */
	else
	{
		usFreeSpace = pxStream->ucRx - pxStream->ucWr - 1 - 4;
	}

	return usFreeSpace;
}

/**
 * @fn
 * @brief
 * @param
 * @return   */
StreamStatusDef xStreamBufferWrite(StreamBufferDef *pxStream, void *pcWriteBuf, uint16 usSzie)
{
	uint32 ulRealWriteSize;
	uint32 inputSize = usSzie;

	uint8 *pcWBuf = NULL;

	/*! Calculation real length. */
	ulRealWriteSize = tlsAlignSizeGet(inputSize);
	// printf("input size:%d , alignment size:%d\r\n" , inputSize, ulRealWriteSize);

	/* 4byte record length. */
	ulRealWriteSize += 4;
	// printf("Real size:%d\r\n" , ulRealWriteSize)  ;

	/* If there is enough space */
	if (usStreamBufferFreeGet(pxStream) < ulRealWriteSize)
	{

		/* error space is not enough. */
		LOG("space is not enough.\r\n");
		return eSTREAM_SPACE_NOT_ENOUGH;
	}
	if(!pxStream->pcBuffer)
	{
		/* error space is not enough. */
		LOG("stream buffer not init\r\n");
		return eSTREAM_BUFFER_IS_INVALID;		
	}

	/* Segment 4bytes , high bit endian.  */
	uint8 move = 24;
	for (uint8 i = 0; i < 4; i++)
	{
		pxStream->pcBuffer[pxStream->ucWr++] = (uint8)(inputSize >> move);
		move -= 8;

		/* write the end. */
		if (pxStream->ucWr >= pxStream->usSize)
			pxStream->ucWr = 0;
	}

	/* Change pointer type to byte. */
	pcWBuf = pcWriteBuf;

	/* Segment contents */
	for (uint16 i = 0; i < inputSize; i++)
	{
		pxStream->pcBuffer[pxStream->ucWr++] = pcWBuf[i];

		/* write the end. */
		if (pxStream->ucWr >= pxStream->usSize)
			pxStream->ucWr = 0;
	}

	/* Segment end,fill 0 */
	uint8 remain = ulRealWriteSize - 4 - inputSize;
	for (uint8 i = 0; i < remain; i++)
	{
		pxStream->pcBuffer[pxStream->ucWr++] = 0;

		/* write the end. */
		if (pxStream->ucWr >= pxStream->usSize)
			pxStream->ucWr = 0;
	}

	/* Item+1 */
	pxStream->ucNfItems++;

	/* return */
	return eSTREAM_NOT_ERR;
}

/**
 * @fn
 * @brief   Maximum
 * @param
 * @return
 */

/**
 * @fn
 * @brief   Get next buffer size.
 * @param
 * @return   */
uint16 usStreamBufferGetNextSize(StreamBufferDef *pxStream)
{
	uint16 usStartPoint = 0;
	uint32 ulItemSize = 0;

	if (pxStream->ucNfItems != 0)
	{

		usStartPoint = pxStream->ucRx;

		for (uint8 i = 0; i < 4; i++)
		{

			ulItemSize <<= 8;
			ulItemSize += pxStream->pcBuffer[usStartPoint++];

			if (usStartPoint >= pxStream->usSize)
			{
				usStartPoint = 0;
			}
		}

		return (uint16)ulItemSize;
	}
	else
	{
		return 0;
	}
}

/**
 * @fn
 * @brief
 * @param
 * @return   */
uint16 usStreamBufferRead(StreamBufferDef *pxStream, void *pcReadBuf)
{
	uint16 usReadSize = usStreamBufferGetNextSize(pxStream);
	uint16 usReadStartPoint;
	uint32 ulRealWriteSize;

	uint8 *pcRdBuf = NULL;

	if (usReadSize != 0)
	{
		usStreamBufferPeek(pxStream,pcReadBuf,0);
		usStreamBufferRead2Next(pxStream);
		/* read size */
		return usReadSize;
	}
	else
	{
		return 0;
	}
}

void usStreamBufferRead2Next(StreamBufferDef *pxStream)
{

	pxStream->ucRx += tlsAlignSizeGet(usStreamBufferGetNextSize(pxStream)) + 4;

	if (pxStream->ucRx >= pxStream->usSize)
		pxStream->ucRx -= pxStream->usSize;
	/*#############################################*/

	/* Number of items */
	pxStream->ucNfItems--;
}
/**
 * @fn
 * @brief   Get stream buffer but not delete.
 * @param	ucNumber 0-max
 *			if item is null ,return length 0.
 *
 *			Example:
 *			Have 10 items , effective input 0-9.
 * @return  relay in buffer size
 */
uint16 usStreamBufferPeek(StreamBufferDef *pxStream, uint8 *pcPeekBuf, uint16 usNumber)
{
	uint16 usNfItems = usStreamBufferNumberOfItemsGet(pxStream);
	uint16 usSpoint;
	uint16 usLength;

	if (usNfItems == 0)
	{
		return 0;
	}
	else if (usNumber >= usNfItems)
	{
		return 0;
	}
	/* effective input */
	else
	{
		usSpoint = pxStream->ucRx;

		/* Find item */
		for (uint16 i = 0; i <= usNumber; i++)
		{

			if (usNumber == i)
			{

				/* Get item length. */
				usLength = prvStreamBufferItemSizeGet(pxStream, usSpoint);

				/* Copy data to peek buffer. */
				if(pcPeekBuf != NULL)
				{
					usSpoint += 4;
					if (usSpoint >= pxStream->usSize)
					{
						usSpoint = 0;
					}
					for (uint16 i = 0; i < usLength; i++)
					{

						pcPeekBuf[i] = pxStream->pcBuffer[usSpoint];
						usSpoint++;

						if (usSpoint >= pxStream->usSize)
						{
							usSpoint = 0;
						}
					}
				}

				return usLength;		//data_len

			} /* Next Object. */
			else
			{
				usSpoint += /* Item real length = alignment(x)+4 , to next item. */
					tlsAlignSizeGet(prvStreamBufferItemSizeGet(pxStream, usSpoint)) + 4;

				/* ring data. */
				if (usSpoint >= pxStream->usSize)
				{
					usSpoint -= pxStream->usSize;
				}
			}
		}

		return 0;
	}
}

/*#############################################
Use example:

	int main(){

	uint16 usSize ;
	uint8 *pcSend;
	char  *pcRec;
	uint16 usRecSize;

	uint32 writeTotal ;
	uint32 readTotal  ;

	StreamBufferDef xTestBuffer ;

	xStreamBufferCreate(&xTestBuffer , 256) ;

	for(uint32 i=0;i<9999999;i++){

		uint8 writeTimes = 1+ (rand() % 4) ;

	   do{
			 size 4-20
			usSize = 4 + (rand()%15)  ;

			pcSend = malloc(usSize) ;

			 fill data
			for(uint8 j=0; j<usSize;j++){
				pcSend[j] = 33 + (rand()%(126-33));
			}

			xStreamBufferWrite(&xTestBuffer , pcSend , usSize ) ;

			writeTotal+= usSize ;

			free(pcSend);

	   }while(--writeTimes);


		while (usStreamBufferNumberOfItemsGet(&xTestBuffer) !=0 ) {

			pcRec     = malloc(usStreamBufferGetNextSize(&xTestBuffer));
			usRecSize = usStreamBufferRead(&xTestBuffer , pcRec);

			readTotal+=usRecSize ;
			free(pcRec);
		}

		if(writeTotal != readTotal){
			printf("error\r\n");
		}
	}

	printf("Hello World\n");
	return 0;
	}

#############################################*/
